package com.ydj.framework.swagger;

import com.ydj.framework.protocol.BaseEnum;
import io.swagger.annotations.ApiModelProperty;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.PropertySpecificationBuilder;
import springfox.documentation.schema.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

@Component
@Primary
@Slf4j
public class SwaggerDocConfig implements ModelPropertyBuilderPlugin {
    /**
     * 是否允许swagger
     */
    @Value("${knife4j.production}")
    private Boolean disable;

    @SneakyThrows
    @Override
    public void apply(ModelPropertyContext context) {
        if (disable) {
            return;
        }
        PropertySpecificationBuilder builder = context.getSpecificationBuilder();
        if (builder == null) {
            return;
        }

        if (!context.getAnnotatedElement().isPresent()) {
            return;
        }
        AnnotatedElement annotatedElement = context.getAnnotatedElement().get();
        if (!(annotatedElement instanceof Field)) {
            return;
        }
        Field field = (Field) annotatedElement;

        // 统一处理响应的必填和选填
        handleResponseRequired(field, builder);

        // 下面处理不同的类型
        if (handleListEnum(field, builder)) {
            return;
        }
        if (handleEnum(field, builder)) {
            return;
        }
        if (handleBoolean(field, builder)) {
            return;
        }
        if (handleDate(field, builder)) {
            return;
        }
    }

    @Override
    public boolean supports(@NotNull DocumentationType documentationType) {
        return true;
    }

    // 响应里，补上必填和选填字段
    private void handleResponseRequired(Field annotatedElement, PropertySpecificationBuilder builder) {
        ApiModelProperty apiModelProperty = annotatedElement.getAnnotation(ApiModelProperty.class);
        if (apiModelProperty == null) {
            return;
        }
        String strRequired = apiModelProperty.required() ? "必填" : "选填";

        String desc = String.format("%s(%s)", builder.build().getDescription(), strRequired);
        builder.description(desc);
    }

    private boolean handleListEnum(Field annotatedElement, PropertySpecificationBuilder builder) {
        if (annotatedElement.getType() != List.class) {
            return false;
        }
        // 当前集合的泛型类型
        Type genericType = annotatedElement.getGenericType();
        if (!(genericType instanceof ParameterizedType)) {
            return false;
        }
        ParameterizedType pt = (ParameterizedType) genericType;
        if (pt.getActualTypeArguments().length <= 0) {
            return false;
        }
        if (!(pt.getActualTypeArguments()[0] instanceof Class)) {
            return false;
        }
        Class<?> actualTypeArgument = (Class<?>) pt.getActualTypeArguments()[0];
        Object[] enumConstants = actualTypeArgument.getEnumConstants();
        if (enumConstants == null || enumConstants.length <= 0) {
            return false;
        }
        if (!(enumConstants[0] instanceof BaseEnum)) {
            return false;
        }

        // 允许值的修改
        ModelSpecification modelSpecification = builder.build().getType();
        if (modelSpecification == null) {
            return false;
        }
        modelSpecification.getCollection().
                flatMap(collectionSpecification -> collectionSpecification.getModel().getFacets()).ifPresent(modelFacets -> {
                    for (ElementFacet item : modelFacets.getFacets()) {
                        if (!(item instanceof EnumerationFacet)) {
                            continue;
                        }
                        // 把原来的删除掉，然后写我的
                        List<String> allowedValues = ((EnumerationFacet) item).getAllowedValues();
                        allowedValues.clear();
                        for (Object enumString : enumConstants) {
                            BaseEnum baseEnum = (BaseEnum) enumString;
                            allowedValues.add(baseEnum.getProtocolName());
                        }
                    }
                });

        // 描述
        List<String> descList = new LinkedList<>();
        for (Object enumConstant : enumConstants) {
            BaseEnum enumObject = (BaseEnum) enumConstant;
            descList.add(enumObject.getProtocolName() + "=" + enumObject.getDesc());
        }

        String desc = builder.build().getDescription() + "。" + String.join(",", descList);
        builder.description(desc);

        return true;
    }

    // 处理枚举，让他自动填补描述和允许值
    private boolean handleEnum(Field annotatedElement, PropertySpecificationBuilder builder) {
        Object[] enumConstants = annotatedElement.getType().getEnumConstants();
        if (enumConstants == null || enumConstants.length <= 0) {
            return false;
        }
        if (!(enumConstants[0] instanceof BaseEnum)) {
            return false;
        }

        // 允许值, 描述
        List<String> allowableList = new LinkedList<>();
        List<String> descList = new LinkedList<>();
        for (Object enumConstant : enumConstants) {
            BaseEnum enumObject = (BaseEnum) enumConstant;
            // 允许值
            allowableList.add(enumObject.getProtocolName());
            // 描述
            descList.add(enumObject.getProtocolName() + "=" + enumObject.getDesc());
        }

        String desc = builder.build().getDescription() + "。" + String.join(",", descList);
        builder.enumerationFacet(enumerationElementFacetBuilder -> enumerationElementFacetBuilder.allowedValues(allowableList))
                .description(desc);

        return true;
    }

    // 处理布尔值，把他变成1和0
    private boolean handleBoolean(Field annotatedElement, PropertySpecificationBuilder builder) {
        if (annotatedElement.getType() != Boolean.class) {
            return false;
        }

        // 修改格式类型，把boolean改成int
        ModelSpecification oldModelSpecification = builder.build().getType();
        if (oldModelSpecification == null) {
            return false;
        }
        ModelSpecification newModelSpecification = new ModelSpecification(oldModelSpecification.getName(),
                oldModelSpecification.getFacets().orElse(null),
                new ScalarModelSpecification(ScalarType.INTEGER),
                oldModelSpecification.getCompound().orElse(null),
                oldModelSpecification.getCollection().orElse(null),
                oldModelSpecification.getMap().orElse(null),
                oldModelSpecification.getReference().orElse(null)
        );
        builder.type(newModelSpecification);

        // 允许值，变成0和1
        List<String> allowableList = Arrays.asList("0", "1");
        builder.enumerationFacet(enumerationElementFacetBuilder -> enumerationElementFacetBuilder.allowedValues(allowableList));

        // 描述
        String desc = builder.build().getDescription() + "。1=是，0=否";
        builder.description(desc);
        return true;
    }

    private boolean handleDate(Field annotatedElement, PropertySpecificationBuilder builder) {
        if (annotatedElement.getType() != Date.class) {
            return false;
        }

        // 修改格式类型，把string（date出来的就是string）改成int64
        ModelSpecification oldModelSpecification = builder.build().getType();
        if (oldModelSpecification == null) {
            return false;
        }
        ModelSpecification newModelSpecification = new ModelSpecification(oldModelSpecification.getName(),
                oldModelSpecification.getFacets().orElse(null),
                new ScalarModelSpecification(ScalarType.LONG),
                oldModelSpecification.getCompound().orElse(null),
                oldModelSpecification.getCollection().orElse(null),
                oldModelSpecification.getMap().orElse(null),
                oldModelSpecification.getReference().orElse(null)
        );
        builder.type(newModelSpecification);

        // 描述
        String desc = builder.build().getDescription() + "。时间戳的单位是毫秒。";
        builder.description(desc);
        return true;
    }
}

